ScrapboxっぽいことをするDenops plugin
改良版
Thanks, /kuuote/kuuote.icon!
あとで読もうっとtakker.icon
/icons/hr.icon
created by /kuuote/kuuote.icon
01:14:01 早速書き換えてる
code:scrap.ts
type PageRelation = {
relate: string;
pages: Page[];
};
mapからkeyに相当する値を取り出し、なかったらfallbackで作る
code:scrap.ts
const getOrCreate = <T>(map: Map<string, T>, key: string, fallback: () => T) => {
const result = map.get(key);
if (result) result;
const newValue = fallback();
map.set(key, newValue);
return newValue;
};
多分一つのページを表すclass
class定義は後置できるんだ。知らなかった
code:scrap.ts
class Page {
static comparator = (a: Page, b: Page) =>
a.mtime === b.mtime ? (a.name < b.name ? -1 : 1) : b.mtime - a.mtime;
name: string;
links = new Map<string, Page>();
fromLinks = new Map<string, Page>();
mtime = 0;
constructor(name: string) {
this.name = name;
}
getLinks(): Array<PageRelation> {
const links = {
relate: "links",
pages: Array.from(this.links.values()).sort(Page.comparator),
};
const fromLinks = {
relate: "fromLinks",
pages: Array.from(this.fromLinks.values()).sort(Page.comparator),
};
const twoHop = links.pages.map((
p,
) => ({
relate: p.name,
pages: Array.from(p.fromLinks.values()).sort(Page.comparator),
}));
const visit = new Map<string, void>();
// ignore this at link destination
visit.set(this.name);
relate,
pages: pages.filter((p) => {
if (visit.has(p.name)) {
return false;
}
visit.set(p.name);
return true;
}),
})).filter(({ pages }) => pages.length !== 0);
}
}
Projectを表すclass
必要なのかな?
Denoobjectは何もimportせず使えるみたい
いや当たり前か。
code:scrap.ts
class Project {
path: string;
pages = new Map<string, Page>();
constructor(path: string) {
this.path = path;
}
getOrCreatePage(name: string) {
return getOrCreate(this.pages, name, () => new Page(name));
}
readAll() {
for (const entry of Deno.readDirSync(this.path)) {
if (!entry.name.endsWith(".scp")) continue;
this.resolve(entry.name.slice(0, -4), ${this.path}/${entry.name});
}
}
リンクをparseして解決している部分
import {parse} from https://github.com/progfay/scrapbox-parser/raw/master/src/parse.ts
objectがネストするから、すべての内部リンク記法を抽出するのが難しくなるか?
forで回せばなんとかなりそう
code:scrap.ts
resolve(name: string, path: string) {
const page = this.getOrCreatePage(name);
// 既存のリンクの削除
for (const linkedPage of page.links.values()) {
linkedPage.fromLinks.delete(name);
}
// テキストを読んで [link] 形式のリンクを抽出し
const match = (Deno.readTextFileSync(path).match(/\^\+\]/g) ?? []).map((
l,
) => l.slice(1, -1));
// ページを関連付けする
for (const link of match) {
const linkToPage = this.getOrCreatePage(link);
page.links.set(link, linkToPage);
linkToPage.fromLinks.set(name, page);
}
}
}
const projects = new Map<string, Project>();
export const getOrCreateProject = (path: string): Project =>
getOrCreate(projects, path, () => new Project(path));
code:scrap_test.ts
import {
assertEquals,
import { getOrCreateProject } from "./scrap.ts";
const hoge = getOrCreateProject("./test");
hoge.readAll();
const a = hoge.getOrCreatePage('a');
const b = hoge.getOrCreatePage('b');
const c = hoge.getOrCreatePage('c');
// resolve link
console.log('# resolve links');
console.log('a -> b');
assertEquals(Array.from(a.links.keys()).sort(), "b"); console.log('b <- a, c');
assertEquals(Array.from(b.fromLinks.keys()).sort(), "a", "c"); console.log('c -> b');
assertEquals(Array.from(c.links.keys()).sort(), "b"); // get links, backlinks, and two hop links
console.log('# get links');
console.log('a');
console.log('links -> b | b -> c')
const aLinks = a.getLinks();
assertEquals(aLinks.length, 2); // links, b
assertEquals(aLinks0.relate, "links"); assertEquals(aLinks0.pages.map((p) => p.name), "b"); assertEquals(aLinks1.relate, "b"); assertEquals(aLinks1.pages.map((p) => p.name), "c"); // ignore self console.log('b');
console.log('fromLinks -> a, c')
const bLinks = b.getLinks();
assertEquals(bLinks.length, 1); // fromLinks
assertEquals(bLinks0.relate, "fromLinks"); assertEquals(bLinks0.pages.map((p) => p.name).sort(), "a", "c"); console.log('c');
console.log('links -> b | b -> a')
const cLinks = c.getLinks();
assertEquals(cLinks.length, 2); // links, b
assertEquals(cLinks0.relate, "links"); assertEquals(cLinks0.pages.map((p) => p.name), "b"); assertEquals(cLinks1.relate, "b"); assertEquals(cLinks1.pages.map((p) => p.name), "a"); // ignore self